import os
import time
from datetime import datetime

import allure
from selenium.common import NoSuchElementException, TimeoutException
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.actions.wheel_input import ScrollOrigin
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.wait import WebDriverWait

from config import setting
from config.operationConfig import OperationConfig
from utils.logging_tool.recordlog import logs


class Keywords:
    """
    关键字封装
    """

    def __init__(self, driver):
        # 初始化浏览器
        self.__driver = driver
        # 显示等待初始化,设置等待最长时间、轮询的频率以及在等待期间遇到的异常处理
        self.__wait = WebDriverWait(self.__driver, setting.WAIT_TIME, setting.POLL_TIME,
                                    ignored_exceptions=[TimeoutException])
        self.conf = OperationConfig()

    """ ======================== 浏览器操作 ==================== """

    def window_max(self):
        """ 浏览器窗口最大化 """
        self.__driver.maximize_window()

    def open(self, **kwargs):
        """ 打开测试页面 """
        url = kwargs['data']
        if url.startswith('http') or url.startswith('https'):
            self.__driver.get(url)
            logs.info(f'打开页面：{url}')
        else:
            new_url = self.conf.get_host('host') + url
            self.__driver.get(new_url)
            logs.info(f'打开页面：{new_url}')

    @property
    def title(self):
        """ 获取页面标题 """
        return self.__driver.title

    @property
    def current_url(self):
        """ 获取当前页面的URL """
        return self.__driver.current_url

    def forward(self):
        """ 浏览器前进按钮 """
        self.__driver.forward()

    def back(self):
        """ 浏览器后退按钮 """
        self.__driver.back()

    def refresh(self):
        """ 页面刷新 """
        self.__driver.refresh()

    def scroll_to_button(self):
        """ 使用JavaScript滚动页面到最底部 """
        self.__driver.execute_script("window.scrollTo(0,document.body.scrollHeight);")

    def close(self):
        """关闭当前窗口"""
        self.__driver.close()

    """ ======================== 查找元素操作 ==================== """

    def find_element_exist(self, by, value):
        """
        【查找元素存在】:并不一定意味着该元素是可见的
        :param by: 定位方式
        :param value: 定位表达式
        :return:
        """
        logs.info(f"正在查找元素<{by} = {value}>")
        # 没有抛出异常的开始时间
        start_time = time.time()
        try:
            element = self.__wait.until(ec.presence_of_element_located((by, value)))
            # 查找结束时间
            end_time = time.time()
            logs.info(f"找到元素<{by} = {value}>，等待时间:{round(end_time - start_time, 2)}秒")
            return element
        except Exception as e:
            logs.error(f'在设定时间内，元素<{by} = {value}>未找到或不存在! err={e}')
            return False

    def find_elements_exist(self, by, value):
        """
        【查找元素列表（所有元素）存在】：find_elements
        :param by: 定位方式
        :param value: 定位表达式
        :return:
        """
        logs.info(f"正在查找页面所有元素<{by} = {value}>")
        # 没有抛出异常的开始时间
        start_time = time.time()
        try:
            elements = self.__wait.until(ec.presence_of_all_elements_located((by, value)))
            # 查找结束时间
            end_time = time.time()
            logs.info(f"找到元素列表<{by} = {value}>，等待时间:{round(end_time - start_time, 2)}秒")
            return elements
        except Exception as e:
            logs.error(f"在设定时间内，元素列表<{by} = {value}>未找到或者不存在")
            raise e

    def find_element_visible(self, by, value):
        """
        【查找元素可见（存在并可见）】:可见性意味着元素不仅会显示，而且高度和宽度也会大于0
        :param by: 定位方式
        :param value: 定位表达式
        :return:
        """
        logs.info(f"正在查找元素<{by} = {value}>")
        # 没有抛出异常的开始时间
        start_time = time.time()
        try:
            element = self.__wait.until(ec.visibility_of_element_located((by, value)))
            # 查找结束时间
            end_time = time.time()
            logs.info(f"找到元素<{by} = {value}>，等待时间:{round(end_time - start_time, 2)}秒")
            return element
        except Exception as e:
            logs.error(f"在设定时间内，元素<{by} = {value}>不可见！err={e}")
            raise False

    def find_element_clickable(self, by, value):
        """
        【查找元素可点击】：判断某个元素中是否可见并且是enable的
        :param by: 定位方式
        :param value: 定位表达式
        :return:
        """
        logs.info(f"正在查找元素<{by} = {value}>")
        # 没有抛出异常的开始时间
        start_time = time.time()
        try:
            element = self.__wait.until(ec.element_to_be_clickable((by, value)))
            # 查找结束时间
            end_time = time.time()
            logs.info(f"找到元素<{by} = {value}>，等待时间:{round(end_time - start_time, 2)}秒")
            return element
        except Exception as e:
            logs.error(f"在设定时间内，元素<{by} = {value}>点击失败！err={e}")
            raise False

    def find_element_selected(self, by, value):
        """
        【查找元素选中】：一般在下拉列表
        :param by: 定位方式
        :param value: 定位表达式
        :return:
        """
        logs.info(f"正在查找元素<{by} = {value}>")
        # 没有抛出异常的开始时间
        start_time = time.time()
        try:
            element = self.__wait.until(ec.element_to_be_selected((by, value)))
            # 查找结束时间
            end_time = time.time()
            logs.info(f"找到元素<{by} = {value}>，等待时间:{round(end_time - start_time, 2)}秒")
            return element
        except Exception as e:
            logs.error(f"在设定时间内，元素<{by} = {value}>选中失败！err={e}")
            raise False

    """ ======================== 输入操作 ==================== """

    def input(self, **kwargs):
        """输入操作"""
        # By.XPATH
        by = kwargs["by"]
        value = kwargs["value"]
        data = kwargs["data"]
        try:
            # 获取元素
            element = self.find_element_exist(by, value)
            # 清空操作，避免输入框内有初始值等
            # element.clear()
            logs.info(f"正在输入元素<{by}:{value}>的文本内容")
            # 输入操作
            element.send_keys(data)
            logs.info(f"元素<{by}:{value}>输入内容成功，输入的内容为：{data}")
        # NoSuchElementException，这个异常通常表示尝试定位页面上不存在的元素
        except NoSuchElementException as e:
            logs.error(f"元素<{by}:{value}>不存在，输入操作失败！ err={e}")
            raise e

    def input_enter(self, **kwargs):
        """输入内容并回车"""
        by = kwargs["by"]
        value = kwargs["value"]
        data = kwargs["data"]
        try:
            # 获取元素
            element = self.find_element_exist(by, value)
            # 清空操作，避免输入框内有初始值等
            element.clear()
            logs.info(f"正在输入元素<{by}:{value}>的文本内容")
            # 输入操作
            element.send_keys(data)
            # 回车
            element.send_keys(Keys.ENTER)
            logs.info(f"元素<{by}:{value}>输入内容成功，输入的内容为：{data}")
        except NoSuchElementException as e:
            logs.error(f"元素<{by}:{value}>不存在，输入操作失败！ err={e}")
            raise e

    """ ======================== 点击操作 ==================== """

    def click(self, **kwargs):
        """点击操作"""
        by = kwargs["by"]
        value = kwargs["value"]
        force = True  # 可选参数，表示是否使用强制点击，默认为False
        try:
            element = self.find_element_exist(by, value)
            logs.info(f"正在点击元素<{by}:{value}>")
            if not force:
                # js 脚本点击方式
                self.__driver.execute_script("arguments[0].click()", element)
            else:
                self.__driver.execute_script("arguments[0].click({force:true})", element)
            logs.info(f"元素<{by}:{value}>点击成功")
        except NoSuchElementException as e:
            logs.error(f"元素<{by}:{value}>不存在，点击失败！err={e}")
            raise e

    """ ======================== 获取元素值操作 ==================== """

    def element_text(self, **kwargs):
        """获取元素文本内容"""
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            element = self.find_element_exist(by, value)
            logs.info(f"正在获取元素<{by}:{value}>的文本内容")
            element_text = element.text
            logs.info(f'获取元素<{by}:{value}>的文本内容为：{element_text}')
            return element_text
        except Exception as e:
            logs.error(f'获取元素<{by}:{value}>的文本内容失败！err={str(e)}')
            raise e

    def elements_text(self, **kwargs):
        """获取所有元素文本内容"""
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            elements = self.find_element_exist(by, value)
            logs.info(f"正在获取元素<{by}:{value}>的所有文本内容")
            elements_text = elements.text
            text_list = []
            for one_text in elements_text:
                text_list.append(one_text)
            logs.info(f'获取元素<{by}:{value}>的文本内容列表为：{elements_text}')
            return elements_text
        except Exception as e:
            logs.error(f'获取元素<{by}:{value}>的所有文本内容失败！err={str(e)}')
            raise e

    def elements_attr(self, **kwargs, ):
        """获取元素属性值"""
        by = kwargs["by"]
        value = kwargs["value"]
        attr_name = kwargs["attr_name"]  # 属性名称
        try:
            element = self.find_element_exist(by, value)
            logs.info(f"准备获取元素<{by}:{value}>的{attr_name}属性值")
            value = element.get_attribute(attr_name)
            logs.info(f"获取元素<{by}:{value}>的{attr_name}属性值为{value}")
            return value
        except Exception as e:
            logs.error(f'获取元素<{by}:{value}>的{attr_name}属性值失败！err={str(e)}')
            raise e

    def innerhttml(self, **kwargs):
        """
        获取元素源码
        innerHTML属性设置或返回表格行的开始和结束标签之间的 HTML，包括标签。
        outerHTML设置或获取对象及其内容的HTML形式，也就是标签和文本内容全都显示出
        """
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            element = self.find_element_exist(by, value)
            logs.info(f"正在获取元素<{by}:{value}>的源码")
            innerhtml = element.get_attribute("innerHTML")
            logs.info(f"获取元素<{by}:{value}>的源码为<{innerhtml}>")
            return innerhtml
        except Exception as e:
            logs.error(f"获取元素<{by}:{value}>的源码失败！err={e}")
            raise e

    """ ======================== 模拟鼠标操作 ==================== """

    def mouse_click(self, **kwargs):
        """鼠标点击"""
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            element = self.find_element_clickable(by, value)
            logs.info(f"鼠标在元素<{by}:{value}>上点击")
            ActionChains(self.__driver).click(element).perform()
            logs.info(f"鼠标在元素<{by}:{value}>上点击成功")
        except Exception as e:
            logs.error(f"鼠标在元素<{by}:{value}>上点击失败！err={e}")
            raise e

    def mouse_click_and_hold(self, **kwargs):
        """鼠标点击并按住不放"""
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            element = self.find_element_clickable(by, value)
            logs.info(f"鼠标正在元素<{by}:{value}>上长按")
            ActionChains(self.__driver).click_and_hold(element).perform()
            logs.info(f"鼠标在元素<{by}:{value}>上长按成功")
        except Exception as e:
            logs.error(f"鼠标在元素<{by}:{value}>上长按失败！err={e}")
            raise e

    def mouse_click_and_hold_move_to(self, **kwargs):
        """拖拽元素上的具体坐标位置"""
        by = kwargs["by"]
        value = kwargs["value"]
        x = kwargs["x"]  # 需要点击的元素上的点的X坐标
        y = kwargs["y"]  # 需要点击的元素上的点的Y坐标
        try:
            element = self.find_element_clickable(by, value)
            logs.info("鼠标正在拖拽元素上的具体坐标位置")
            ActionChains(self.__driver).move_to_element_with_offset(element, x, y).click_and_hold().release().perform()
            logs.info("鼠标拖拽元素上的具体坐标位置成功")
        except Exception as e:
            logs.error("鼠标拖拽元素上的具体坐标位置失败！err={e}")
            raise e

    def mouse_click_and_hold_drag_and_drop(self, **kwargs):
        """拖拽元素上的具体坐标位置"""
        by = kwargs["by"]
        value = kwargs["value"]
        x = kwargs["x"]  # 需要拖拽元素点的X坐标
        y = kwargs["y"]  # 需要拖拽元素点的Y坐标
        to_x = kwargs["to_x"]  # 拖拽元素点目标位置的X坐标
        to_y = kwargs["to_y"]  # 拖拽元素点目标位置的Y坐标

        try:
            element = self.find_element_clickable(by, value)
            logs.info("鼠标正在拖拽元素上的具体坐标位置")
            ActionChains(self.__driver).move_to_element_with_offset(element, x, y).click_and_hold().move_by_offset(to_x,
                                                                                                                   to_y).release().perform()
            logs.info("鼠标拖拽元素上的具体坐标位置成功")
        except Exception as e:
            logs.error("鼠标拖拽元素上的具体坐标位置失败！err={e}")
            raise e

    def mouse_right_click(self, **kwargs):
        """鼠标单击右键"""
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            element = self.find_element_clickable(by, value)
            logs.info(f"鼠标正在元素<{by}:{value}>上右击")
            ActionChains(self.__driver).context_click(element).perform()
            logs.info(f"鼠标在元素<{by}:{value}>上右击成功")
        except Exception as e:
            logs.error(f"鼠标在元素<{by}:{value}>上右击失败！err={e}")
            raise e

    def mouse_double_click(self, **kwargs):
        """鼠标双击"""
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            element = self.find_element_clickable(by, value)
            logs.info(f"鼠标正在元素<{by}:{value}>上右击")
            ActionChains(self.__driver).double_click(element).perform()
            logs.info(f"鼠标在元素<{by}:{value}>上双击成功")
        except Exception as e:
            logs.error(f"鼠标在元素<{by}:{value}>上双击失败！err={e}")
            raise e

    def mouse_hover_actions(self, **kwargs):
        """鼠标悬停展示内容或弹框"""
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            move_element = self.find_element_exist(by, value)
            logs.info(f"鼠标正在元素<{by}:{value}>上悬停")
            ActionChains(self.__driver).move_to_element(move_element).perform()
            logs.info('鼠标在元素<{by}:{value}>上悬停成功')
        except Exception as e:
            logs.error(f"鼠标在元素<{by}:{value}>上悬停失败！err={e}")
            raise e

    def mouse_move_to_element(self, **kwargs):
        """鼠标移动到指定位置"""
        by = kwargs["by"]
        value = kwargs["value"]
        x = kwargs["x"]  # 元素点的X坐标
        y = kwargs["y"]  # 元素点的Y坐标
        try:
            move_element = self.find_element_exist(by, value)
            logs.info("鼠标正在移动到指定位置")
            ActionChains(self.__driver).move_to_element_with_offset(move_element, x, y).perform()
            logs.info('鼠标移动到指定位置成功')
        except Exception as e:
            logs.error(f"鼠标移动到指定位置失败！err={e}")
            raise e

    def mouse_move_to_element_click(self, **kwargs):
        """左键点击元素上的具体坐标位置：鼠标移动到指定位置并左键点击"""
        by = kwargs["by"]
        value = kwargs["value"]
        x = kwargs["x"]  # 元素点的X坐标
        y = kwargs["y"]  # 元素点的Y坐标
        try:
            move_element = self.find_element_exist(by, value)
            logs.info("鼠标正在移动到指定位置并点击")
            ActionChains(self.__driver).move_to_element_with_offset(move_element, x, y).click().perform()
            logs.info('鼠标移动到指定位置并点击成功')
        except Exception as e:
            logs.error(f"鼠标移动到指定位置并点击失败！err={e}")
            raise e

    def mouse_move_to_element_rigth_click(self, **kwargs):
        """右键点击元素上的具体坐标位置：鼠标移动到指定位置并右键点击"""
        by = kwargs["by"]
        value = kwargs["value"]
        x = kwargs["x"]  # 元素点的X坐标
        y = kwargs["y"]  # 元素点的Y坐标
        try:
            move_element = self.find_element_exist(by, value)
            logs.info("鼠标正在移动到指定位置并右键点击")
            ActionChains(self.__driver).move_to_element_with_offset(move_element, x, y).context_click().perform()
            logs.info('鼠标移动到指定位置并右键点击成功')
        except Exception as e:
            logs.error(f"鼠标移动到指定位置并右键点击失败！err={e}")
            raise e

    def mouse_press_move(self, **kwargs):
        """鼠标拖动(将目标拖动到指定的位置)：x 和 y 是相对于 source 左上角为原点的偏移量"""
        by = kwargs["by"]
        value = kwargs["value"]
        x = kwargs["x"]  # 元素点的X坐标.横坐标
        y = kwargs["y"]  # 元素点的Y坐标.纵坐标
        try:
            source_element = self.find_element_exist(by, value)
            logs.info(f"鼠标正在将目标拖动到指定的位置")
            ActionChains(self.__driver).drag_and_drop_by_offset(source_element, x, y).perform()
            logs.info(f"鼠标把源元素<{by}:{value}>拖动到横坐标<{x}>纵坐标<{y}>")
        except Exception as e:
            logs.error(f"鼠标把源元素<{by}:{value}>拖动到横坐标<{x}>纵坐标<{y}>失败！err={e}")
            raise e

    def mouse_drag_and_drop(self, **kwargs):
        """鼠标在元素上拖放——在source源元素上单击并按住，移动到target目标元素的位置，然后释放鼠标"""
        # 源元素
        source_by = kwargs["source_by"]
        source_value = kwargs["source_value"]
        # 目标元素
        target_by = kwargs["target_by"]
        target_value = kwargs["target_value"]

        try:
            dragable = self.find_element_exist(source_by, source_value)
            dropable = self.find_element_exist(target_by, target_value)
            logs.info(f"鼠标正在将目标拖动到指定的位置")
            ActionChains(self.__driver).drag_and_drop(dragable, dropable).perform()
            logs.info(f"鼠标把元素<{source_by}:{source_value}>拖动到元素<{target_by}:{target_value}>")
        except Exception as e:
            logs.error(f"鼠标把元素<{source_by}:{source_value}>拖动到元素<{target_by}:{target_value}>失败！err={e}")
            raise e

    """ ======================== 模拟键盘操作 ==================== """

    def enter_actions(self):
        """键盘回车的操作"""
        try:
            ActionChains(self.__driver).send_keys(Keys.ENTER).perform()
            logs.info('按下回车键')
        except NoSuchElementException as e:
            logs.error(f"元素无法定位：{e}")
            raise e

    def input_actions(self, **kwargs):
        """模拟键盘的输入操作"""
        by = kwargs["by"]
        value = kwargs["value"]
        data = kwargs["data"]
        try:
            element = self.find_element_exist(by, value)
            ActionChains(self.__driver).move_to_element(element).click().send_keys(data).perform()
            logs.info(f"元素被输入内容：<{by}:{value}>，输入的内容为：{data}")
        except NoSuchElementException as e:
            logs.error(f"元素无法定位：{e}")
            raise e

    """ ======================== 模拟鼠标滚轮操作 ==================== """

    def scroll_to_element(self, **kwargs):
        """ 滚轮滚动到元素 """
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            iframe = self.find_element_visible(by, value)
            logs.info(f"滚轮正在滚动到元素<{by}:{value}>")
            ActionChains(self.__driver).scroll_to_element(iframe).perform()
            logs.info(f"滚轮滚动到元素<{by}:{value}>成功")
        except Exception as e:
            logs.error(f"滚轮滚动到元素<{by}:{value}>失败！err={e}")
            raise e

    def scroll_by_given_amount(self, **kwargs):
        """滚轮按给定数值滚动,elta x 和 delta y 值，表示向右和向下滚动的量。负值分别代表左和上"""
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            footer = self.find_element_visible(by, value)
            # rect 显示元素在浏览器页面中的坐标位置具体值,在开发者工具里，查看元素右侧的Properties信息：offsetTop （高） 、offsetWidth （宽）
            delta_y = footer.rect['y']
            logs.info(f"滚轮正在按给定数值滚动")
            ActionChains(self.__driver).scroll_by_amount(0, delta_y).perform()
            logs.info(f"滚轮按给定数值滚动成功")
        except Exception as e:
            logs.error(f"滚轮按给定数值滚动失败！err={e}")
            raise e

    def scroll_element_by_given_amount(self, **kwargs):
        """ 滚轮基于元素按给定数值滚动 """
        by = kwargs["by"]
        value = kwargs["value"]
        # x = kwargs["x"]  # offsetTop （高）,可以在开发者工具里，查看元素右侧的Properties信息中的
        # y = kwargs["y"]  # offsetWidth （宽）
        try:
            iframe = self.find_element_visible(by, value)
            # 起点位置
            scroll_origin = ScrollOrigin.from_element(iframe)
            # 如果元素超出视窗，它将滚动到屏幕底部，然后页面将滚动提供的 delta_x 和 delta_y 值。
            logs.info(f"滚轮正在基于元素按给定数值滚动")
            ActionChains(self.__driver).scroll_from_origin(scroll_origin, 0, 200).perform()
            logs.info(f"滚轮基于元素按给定数值滚动成功")
        except Exception as e:
            logs.error(f"滚轮基于元素按给定数值滚动失败！err={e}")
            raise e

    """ ======================== 警告框操作 ==================== """

    @property
    def alert(self):
        """
        【alerts】警告框
        :return: 等待直到页面上出现弹框，存在就返回Alert对象，不存在就返回False
        """
        return self.__wait.until(ec.alert_is_present())

    def alert_confirm(self):
        """ 点击【alert】弹框中确定操作 """
        self.alert.accept()

    def alert_cancel(self):
        """ 点击【alert】弹框中取消操作 """
        self.alert.dismiss()

    def alert_input(self, **kwargs):
        """ 在提示框中输入文本操作 """
        data = kwargs["data"]  # 输入文本的值
        self.alert.send_keys(data)

    """ ======================== 【select】下拉框操作 ==================== """

    def select_action(self, **kwargs):
        """【select】下拉框操作：index 下标、value 值、text 显示文本"""
        by = kwargs["by"]
        value = kwargs["value"]
        content = kwargs["content"]  # select_by_方法的入参
        select_type = kwargs["select_type"]  # select类型（index、value、text）

        try:
            element = self.find_element_visible(by, value)
            logs.info(f"正在根据元素<{by}:{value}>以{select_type}方式进行下拉框选择")
            if select_type == 'index':
                Select(element).select_by_index(content)
                logs.info(f'{select_type}方式选择下拉菜单第{content}条数据')
            elif select_type == 'value':
                Select(element).select_by_value(content)
                logs.info(f'{select_type}方式选择下拉菜单第{content}条数据')
            elif select_type == 'text':
                Select(element).select_by_visible_text(content)
                logs.info(f'{select_type}方式选择下拉菜单第{content}条数据')
            else:
                logs.error(f"不支持{select_type},请确认Select的类型")
        except Exception as e:
            logs.error(f"元素<{by}:{value}>以{select_type}方式进行下拉选择失败！err={e}")
            raise e

    """ ======================== 【radio】单选框操作 ==================== """

    def radio(self, **kwargs):
        """【radio】单选框操作"""
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            element = self.find_element_exist(by, value)
            logs.info(f"正在根据元素<{by}:{value}>进行单选框选择")
            element.click()
            logs.info(f"元素<{by}:{value}>单选框选择成功")
        except Exception as e:
            logs.error(f"元素<{by}:{value}>单选框选择失败！err={e}")
            raise e

    """ ======================== 【checkbox】复选框操作 ==================== """

    def checkboxs(self, **kwargs):
        """checkbox 全部勾选 """
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            element = self.find_element_visible(by, value)
            logs.info(f"正在定位页面所有checkbox元素<{by}:{value}>")
            checkboxs = self.click(element)
            # 循环获取每一个元素并勾选
            for element in checkboxs:
                if element.get_attribute('type') == 'checkbox' and element.is_selected() == False:
                    element.click()
            logs.info(f"element_click<{by}:{value}>成功")
        except Exception as e:
            logs.error(f"定位选择所有checkbox元素<{by}:{value}>失败！err={e}")
            raise e

    def checkbox(self, **kwargs):
        """checkbox 勾选一个"""
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            element = self.find_element_visible(by, value)
            logs.info(f"正在定位checkbox元素<{by}:{value}>")
            # 点击勾选
            self.click(element)
            logs.info(f"element_click<{by}:{value}>成功")
        except Exception as e:
            logs.error(f"定位选择checkbox元素<{by}:{value}>失败！err={e}")
            raise e

    """ ======================== iframe 窗口操作 ==================== """

    # 通过函数名直接调用
    @property
    def switch_to(self):
        """ 切换switch_to """
        return self.__driver.switch_to

    def switch_to_iframe(self, **kwargs):
        """ 切换到iframe内联框架中,元素（index\id\name\iframe元素）定位 """
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            # 判断frame是否可见可切换
            iframe = self.__wait.until(ec.frame_to_be_available_and_switch_to_it(by, value))
            logs.info(f"正在根据元素<{by}:{value}>切换到iframe内联框架中")
            self.switch_to.frame(iframe)
            logs.info(f'切换到{iframe}--iframe内部框架中')
        except Exception as e:
            logs.error(f'切换iframe框架失败！err={e}')

    def exit_iframe(self):
        """ 退出iframe框架(切换iframe到main页面，切出frame框架-默认目录[返回最外层]) """
        logs.info(f"正在退出iframe框架中")
        try:
            self.switch_to.default_content()
            logs.info(f"退出iframe框架，返回到main页面")
        except Exception as e:
            logs.error("退出iframe框架失败！err={e}")
            raise e

    def switch_to_parent(self):
        """ 切出frame框架-上一级 """
        logs.info("正在切换iframe到上一层页面")
        try:
            self.switch_to.parent_frame()
            logs.info("切换iframe到上一层页面成功")
        except Exception as e:
            logs.error("切换iframe到上一层页面失败！err={e}")
            raise e

    """ ======================== 切换 windows 窗口 操作 ==================== """

    def new_tab(self):
        """ 浏览器打开新的标签页，切换窗口句柄 """

        try:
            # 获取所有窗口的句柄列表
            all_window = self.__driver.window_handles
            logs.info(f"获取所有打开的窗口：{all_window}")
            # 获取当前窗口的句柄作为原始窗口
            original_window = self.__driver.current_window_handle
            logs.info(f"获取原始的窗口：{original_window}")
            new_window = None
            for window in all_window:
                # 找到不等于原始窗口句柄的新窗口句柄
                if window != original_window:
                    new_window = window
                    break
            logs.info(f"浏览器正在打开新的标签页")
            if new_window:
                # 切换到新窗口，以便在新窗口上执行后续操作
                self.switch_to.window(new_window)
                logs.info('成功切换到新标签页')
        except TimeoutException:
            logs.error('等待新标签打开超时')
        except NoSuchElementException:
            logs.error('未找到新标签页句柄')
        except Exception as e:
            logs.error(f'切换窗口时发生异常：{str(e)}')

    def switch_to_tab_by_index(self, **kwargs):
        """ 当需要切换多个标签页时 """
        index = kwargs["index"]  # 标签页的索引，从0开始
        try:
            # 获取所有窗口的句柄列表
            all_window = self.__driver.window_handles
            logs.info(f"获取所有打开的窗口：{all_window}")
            if 0 <= index < len(all_window):
                # 获取目标窗口句柄
                target_window = all_window[index]
                logs.info(f"浏览器正在打开多个标签页")
                # 切换到目标窗口
                self.switch_to.window(target_window)
                logs.info(f'成功切换到第{index + 1}个标签页')
            else:
                logs.error('指定的窗口索引超出范围')
        except Exception as e:
            logs.error(f'切换窗口时发生异常：err={str(e)}')

    """ ======================== 截图操作 ==================== """

    def screenshot(self, **kwargs):
        """ 截图-文件 """
        screenshot_name = kwargs["screenshot_name"]  # 文件名
        # 获取当前的时间
        logs.info("正在进行截图")
        current_time = datetime.now().strftime("%Y%m%d%H%M%S")
        file_name = f'{screenshot_name}-{current_time}.png'
        file_path = os.path.join(setting.FILE_PATH.get('IMAGE'), file_name)
        self.__driver.get_screenshot_as_file(file_path)

    def screenshot_png(self):
        """页面截屏，保存为PNG格式"""
        logs.info("正在进行截图保存")
        allure.attach(self.__driver.get_screenshot_as_png(), "截图", allure.attachment_type.PNG)
        return self.__driver.get_screenshot_as_png()

    """ ======================== 文件上传操作操作 ==================== """

    def file_upload(self, **kwargs):
        """ 文件上传：input标签文件选择 """
        by = kwargs["by"]
        value = kwargs["value"]
        data = kwargs["data"]  # 要上传的文件路径
        logs.info("正在进行文件上传操作")
        try:
            # 查找定位文件上传元素
            self.find_element_exist(by, value).send_keys(data)
            logs.info("文件上传成功")
        except Exception as e:
            logs.error(f"文件上传失败！err={e}")

    """ ======================== 断言操作 ==================== """

    def assert_present(self, **kwargs):
        """ 断言元素存在(判断是否为True) """
        by = kwargs["by"]
        value = kwargs["value"]
        try:
            element = self.__driver.find_element(by, value)
            logs.info("正在进行断言元素存在")
            assert element.is_displayed(), '元素不存在'
        except NoSuchElementException as e:
            logs.error(f'元素未找到，{e}')
            raise AssertionError('元素不存在')

    def assert_not_visible(self, **kwargs):
        """ 断言元素不可见或不存在 """
        by = kwargs["by"]
        value = kwargs["value"]
        logs.info("正在进行断言元素不可见或不存在")
        try:
            self.__wait.until(ec.invisibility_of_element_located(by, value))
            logs.info("断言成功，元素不可见或不存在")
        except TimeoutException:
            logs.error('断言失败！元素可见')
            raise

    def assert_title(self, **kwargs):
        """ 断言预期标题文本是否包含(in)在实际文本页面的标题中 """
        expect_title = kwargs["expect_title"]  # 预期文本标题
        logs.info("正在进行断言预期标题文本包含在实际文本页面的标题中")
        try:
            assert expect_title in self.title
            logs.info(f'断言成功！')
        except Exception as e:
            logs.error(f"断言失败！err = {e}")
            raise

    def assert_clickable(self, **kwargs):
        """ 断言元素是否可被点击操作 """
        by = kwargs["by"]
        value = kwargs["value"]
        logs.info("正在进行断言元素可被点击操作")
        try:
            element = self.__wait.until(ec.element_to_be_clickable(by, value))
            # 执行断言或其他操作
            assert element.is_enabled(), "元素未启用交互"
            logs.info('断言结果：元素是可点击的，并且可以进行交互')
            # 例如，点击元素
            element.click()
        except Exception as e:
            logs.error(f"断言失败！err = {e}")
            raise

    def assert_alert(self):
        """断言页面是否出现alert弹框"""
        logs.info("正在进行断言页面是否出现alert弹框")
        try:
            alert = self.__wait.until(ec.alert_is_present())
            logs.info(f'断言成功！')
            return True
        except TimeoutException:
            logs.info(f'断言失败！')
            return False

    def assert_text(self, **kwargs):
        """断言【实际获取到的元素文本值】是否包含【期望结果】"""
        by = kwargs["by"]
        value = kwargs["value"]
        expect = kwargs["expect"]  # 期望结果

        logs.info("正在进行断言【实际获取到的元素文本值】是否包含【期望结果】")

        try:
            result = self.find_element_exist(by, value).text
            assert expect in result
            logs.info(f'断言成功！【期望结果】：{expect} =【实际结果】：{result}')
        except Exception as e:
            logs.error(f"断言失败！err = {e}")
            raise

    def assert_url(self, **kwargs):
        """
        断言当前页面的URL是否与预期URL匹配
        """
        expect = kwargs["expect"]
        # 使用WebDriverWait来确保URL在某个时间点之后与预期匹配
        try:
            result = self.current_url
            assert result == expect
            logs.info(f'断言成功！【期望结果】：{expect} =【实际结果】：{result}')
        except Exception as e:
            logs.error(f"断言失败！err = {e}")
